package com.tang.future;

import com.tang.msg.Request;
import com.tang.msg.Response;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;

import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 异步写
 *
 * @author fulin.tang
 * @version V3.0
 * @since 2022/5/19 14:46
 */
public class SyncWrite {

    public Response writeAndSync(final Channel channel, final Request request, final long timeout) throws Exception {
        if (null == channel) {
            throw new NullPointerException("channel");
        }
        if (null == request) {
            throw new NullPointerException("request");
        }
        if (timeout <= 0) {
            throw new IllegalArgumentException("timeout <= 0");
        }
        String requestId = UUID.randomUUID().toString();
        request.setRequestId(requestId);

        WriteFuture<Response> future = new SyncWriteFuture(requestId);
        SyncWriteMap.getInstance().syncKey.put(requestId, future);

        Response response = doWriteAndSync(channel, request, timeout, future);

        SyncWriteMap.getInstance().syncKey.remove(requestId);

        return response;
    }

    private Response doWriteAndSync(final Channel channel, final Request request, final long timeout, final WriteFuture<Response> writeFuture) throws Exception {

        channel.writeAndFlush(request).addListener((ChannelFutureListener) future -> {
            writeFuture.setWriteResult(future.isSuccess());
            writeFuture.setCause(future.cause());
            // 失败移除
            if (!writeFuture.isWriteSuccess()) {
                SyncWriteMap.getInstance().syncKey.remove(request.getRequestId());
            }
        });

        Response response = writeFuture.get(timeout, TimeUnit.MILLISECONDS);

        if (response == null) {
            if (writeFuture.isTimeout()) {
                throw new TimeoutException();
            } else {
                throw new Exception(writeFuture.cause());
            }
        }
        return response;
    }

}
